package qc.module.duty.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.TimeRangeLengthEnum;
import qc.common.core.exception.QCPromptException;
import qc.common.core.table.QCTable;
import qc.common.core.table.QCTableCell;
import qc.common.core.table.QCTableRow;
import qc.common.core.utils.DateUtil;
import qc.module.duty.dto.group.DutyGroupDto;
import qc.module.duty.dto.staff.DutyStaffDto;
import qc.module.duty.entity.DutySchedule;
import qc.module.duty.entity.DutyScheduleMember;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 值班展示使用Service
 *
 * @author QuCheng Tech
 * @since 2023/8/11
 */
@Service
public class DutyDisplayService {
    private DutyGroupService dutyGroupService;

    @Autowired
    public void setDutyGroupService(DutyGroupService dutyGroupService) {
        this.dutyGroupService = dutyGroupService;
    }

    private DutyScheduleService dutyScheduleService;

    @Autowired
    private void setDutyScheduleService(DutyScheduleService dutyScheduleService) {
        this.dutyScheduleService = dutyScheduleService;
    }

    private DutyStaffService dutyStaffService;

    @Autowired
    public void setDutyStaffService(DutyStaffService dutyStaffService) {
        this.dutyStaffService = dutyStaffService;
    }

    private DutyScheduleMemberService dutyScheduleMemberService;

    @Autowired
    public void setDutyScheduleMemberService(DutyScheduleMemberService dutyScheduleMemberService) {
        this.dutyScheduleMemberService = dutyScheduleMemberService;
    }

    /***
     * 根据指定的起止时间和部门ID集合查询值班表，以Table形式展示最终的值班表结果
     *
     * @param timeRangeLengthEnum 查询时间段枚举
     * @param date 起始日期
     * @param deptids 查询的部门ID结合
     * @param deptIdAndNames 部门ID和对应的名称集合
     * @return qc.common.core.table.QCTable
     * @author QuCheng Tech
     * @since 2023/8/11
     */
    public QCTable queryDutyDisplayTable(TimeRangeLengthEnum timeRangeLengthEnum, Date date, int[] deptids, Map<Integer, String> deptIdAndNames) throws QCPromptException {
        //查询时间处理，根据时段类型枚举和输入的日期计算查询的开始和截止日期
        String timeString = DateUtil.getString(date, "yyyy年");
        Date beginTime = date;
        Date endTime = date;
        if (timeRangeLengthEnum == TimeRangeLengthEnum.MONTH) {
            beginTime = DateUtil.newDate(DateUtil.getYear(date), DateUtil.getMonth(date) - 1, 0x1);
            endTime = DateUtil.addDays(DateUtil.addMonths(beginTime, 0x1), -1);
            timeString = DateUtil.getString(date, "yyyy年M月");
        } else if (timeRangeLengthEnum == TimeRangeLengthEnum.QUARTER) {
            beginTime = DateUtil.newDate(DateUtil.getYear(date), 0x0, 0x1);
            int month = DateUtil.getMonth(date);
            if (month >= 10)
                beginTime = DateUtil.newDate(DateUtil.getYear(date), 0x9, 0x1);
            else if (month >= 7)
                beginTime = DateUtil.newDate(DateUtil.getYear(date), 0x6, 0x1);
            else if (month >= 4)
                beginTime = DateUtil.newDate(DateUtil.getYear(date), 0x3, 0x1);
            endTime = DateUtil.addDays(DateUtil.addMonths(beginTime, 0x3), -1);
            if (DateUtil.getMonth(beginTime) == 0x1)
                timeString += "第一季度";
            else if (DateUtil.getMonth(beginTime) == 0x4)
                timeString += "第二季度";
            else if (DateUtil.getMonth(beginTime) == 0x7)
                timeString += "第三季度";
            else if (DateUtil.getMonth(beginTime) == 0xA)
                timeString += "第四季度";
        } else {
            //默认按年显示
            beginTime = DateUtil.newDate(DateUtil.getYear(date), 0x0, 0x1);
            endTime = DateUtil.addDays(DateUtil.addYears(beginTime, 0x1), -1);
        }
        //查询和处理步骤
        //1.先根据部门ID集合查询值班分组信息
        //2.根据起止时间和值班分组信息查询排班表记录
        //3.如果有对应的排班信息，查询出所有的值班人员信息和值班排班人员表中信息
        //4.根据返回结果以Table形式展示要求，按照最小排班时段、处理每个分组的排班人员信息进行返回
        if (beginTime.after(endTime))
            throw new QCPromptException("查询排班信息的起始时间不能大于截止时间");
        if (deptids == null || deptids.length < 0x1)
            throw new QCPromptException("查询排班信息的部门不能为空");

        //步骤1.根据部门ID结合查询值班分组信息
        List<DutyGroupDto> groups = dutyGroupService.query(Arrays.stream(deptids).boxed().collect(Collectors.toList()), deptIdAndNames);
        if (groups == null || groups.size() < 0x1)
            throw new QCPromptException("根据部门得到的值班分组信息为空");

        //步骤2.根据起止时间和值班分组信息查询排班表记录
        List<DutySchedule> schedules = dutyScheduleService.querySchedules(beginTime, endTime, groups.stream().map(DutyGroupDto::getId).collect(Collectors.toList()));

        //步骤3.如果有排班记录，查询出所有的值班人员信息和值班排班人员表中的信息
        List<DutyStaffDto> allDutyStaffs = null;
        List<DutyScheduleMember> allScheduleMembers = null;
        if (schedules != null && schedules.size() > 0x0) {
            allDutyStaffs = dutyStaffService.getAllStaffs();
            allScheduleMembers = dutyScheduleMemberService.getAllScheduleMembers();
        }

        //步骤4，处理返回结果
        QCTable result = new QCTable();

        //根据值班分组信息计算表格列数
        int totalGroupCol = 0x0;
        StringBuilder sbDeptNames = new StringBuilder();
        for (Integer deptId : deptids) {
            if (deptIdAndNames != null && deptIdAndNames.size() > 0x0 && deptIdAndNames.containsKey(deptId)) {
                sbDeptNames.append(deptIdAndNames.get(deptId) + "、");
            }
        }
        if (sbDeptNames != null && sbDeptNames.length() > 0x0)
            sbDeptNames.deleteCharAt(sbDeptNames.length() - 0x1);
        StringBuilder sbGroupNames = new StringBuilder();
        for (DutyGroupDto group : groups) {
            int groupCol = 0x1;
            if (group != null && group.getCol() > 0x0)
                groupCol = group.getCol();

            totalGroupCol += groupCol;
            sbGroupNames.append(group.getName() + "、");
        }
        if (sbGroupNames != null && sbGroupNames.length() > 0x0)
            sbGroupNames.deleteCharAt(sbGroupNames.length() - 0x1);
        //表格列：前面显示起止日期和周数/节假日的列数
        int tableDateAndWeekCol = 0x2;
        //每个值班成员显示信息的列数，姓名、手机号、短信
        int memberInfoColumns = 0x3;

        //生成表头行
        List<QCTableRow> headerRows = new ArrayList<>();
        //标题行
        String beginDateString = DateUtil.getDateString(beginTime);
        String endDateString = DateUtil.getDateString(endTime);
        QCTableRow headerTitleRow = new QCTableRow();
        List<QCTableCell> cells = new ArrayList<>();
        QCTableCell cell = new QCTableCell();
        cell.setRowspan(0x1);
        cell.setColspan(totalGroupCol * memberInfoColumns + tableDateAndWeekCol);
        //标题名称：XXX部门 起止日期 值班安排
        cell.setText(sbDeptNames.toString() + timeString + "值班安排");
        cells.add(cell);
        headerTitleRow.setCells(cells);
        headerRows.add(headerTitleRow);
        //分组行
        QCTableRow headerGroupRow = new QCTableRow();
        //成员行
        QCTableRow headerMemberRow = new QCTableRow();
        List<QCTableCell> groupCells = new ArrayList<>();
        List<QCTableCell> memberCells = new ArrayList<>();
        cell = new QCTableCell();
        cell.setRowspan(0x2);
        cell.setColspan(0x1);
        cell.setText("日期");
        groupCells.add(cell);
        cell = new QCTableCell();
        cell.setRowspan(0x2);
        cell.setColspan(0x1);
        cell.setText("星期");
        groupCells.add(cell);
        for (DutyGroupDto group : groups) {
            int groupCol = group.getCol();
            //分组行
            cell = new QCTableCell();
            cell.setRowspan(0x1);
            cell.setColspan(memberInfoColumns * groupCol);
            cell.setText(group.getName());
            //可以根据不同分组指定显示的样式
            //if (group.getName().equalsIgnoreCase("生产组"))
            //    cell.setStyle("background-color: pink;");

            groupCells.add(cell);
            //成员行
            for (int i = 1; i <= groupCol; i++) {
                QCTableCell memberCell = new QCTableCell();
                memberCell.setRowspan(0x1);
                memberCell.setColspan(0x1);
                memberCell.setText("姓名");
                memberCells.add(memberCell);

                memberCell = new QCTableCell();
                memberCell.setRowspan(0x1);
                memberCell.setColspan(0x1);
                memberCell.setText("手机号");
                memberCells.add(memberCell);

                memberCell = new QCTableCell();
                memberCell.setRowspan(0x1);
                memberCell.setColspan(0x1);
                memberCell.setText("短号");
                memberCells.add(memberCell);
            }
        }
        headerGroupRow.setCells(groupCells);
        headerMemberRow.setCells(memberCells);
        headerRows.add(headerGroupRow);
        headerRows.add(headerMemberRow);
        result.setHeaderRows(headerRows);

        //表格数据行信息
        if (schedules != null && schedules.size() > 0x0) {
            int index = 0x0;
            //根据排班信息得到最小的排班时段，按最小排班时段显示
            Map<Date, Integer> indexAndBeginTimes = new Hashtable<>();
            Map<Integer, Date> indexAndEndTimes = new Hashtable<>();

            for (DutySchedule schedule : schedules) {
                Date scheduleBeginTime = schedule.getBegintm();
                Date scheduleEndTime = schedule.getEndtm();
                //如果得到的班次起止时间超过了查询值班表的范围，以查询值班表的时间范围为准
                if (scheduleBeginTime.before(beginTime))
                    scheduleBeginTime = beginTime;
                if (scheduleEndTime.after(endTime))
                    scheduleBeginTime = endTime;
                //如果有已存在的对应的开始时间和结束时间，不需要对时间段进行分段处理；如果开始时间相同，结束时间不同需要更新已有的结束时间并添加新的时间段
                boolean beginTimeHasExist = indexAndBeginTimes.containsKey(scheduleBeginTime);
                if (beginTimeHasExist) {
                    //开始时间已存在，判断结束时间是否相同
                    int existIndex = indexAndBeginTimes.get(scheduleBeginTime);
                    Date existEndTime = indexAndEndTimes.get(existIndex);

                    if (!scheduleEndTime.equals(existEndTime)) {
                        //2个时间不相同
                        //判断如果已有的时间小于截止时间，需要将已有的截止时间按当前截止时间进行分段；
                        //判断如果已有的时间大于截止时间，按当前截止时间更新已有记录，并以当前截止时间作为新的开始
                        if (existEndTime.before(scheduleEndTime)) {
                            //以当前截止时间最为已有记录的截止时间，并以当前截止时间为开始添加新记录
                            indexAndEndTimes.remove(existIndex);
                            indexAndEndTimes.put(existIndex, scheduleEndTime);
                            index++;
                            indexAndBeginTimes.put(existEndTime, index);
                            indexAndEndTimes.put(index, scheduleEndTime);
                            index++;
                        } else {
                            //按当前截止时间更新已有记录，并以当前截止时间作为新的开始
                            indexAndEndTimes.remove(existIndex);
                            indexAndEndTimes.put(existIndex, scheduleEndTime);
                            index++;
                            indexAndBeginTimes.put(scheduleEndTime, index);
                            indexAndEndTimes.put(index, existEndTime);
                            index++;
                        }
                    }
                } else {
                    //开始时间不存在，添加当前
                    indexAndBeginTimes.put(scheduleBeginTime, index);
                    indexAndEndTimes.put(index, scheduleEndTime);
                    index++;
                }
            }

            List<QCTableRow> bodyRows = new ArrayList<>();
            //遍历记录的每段值班起止时间，获取每个分组的排班记录
            //indexAndBeginTimes其中的时间先后顺序可能打乱，需要重新排序
            List<Date> orderTimes = indexAndBeginTimes.keySet().stream().sorted().collect(Collectors.toList());
            for (Date scheduleBeginTime : orderTimes) {
                Integer scheduleIndex = indexAndBeginTimes.get(scheduleBeginTime);
                Date scheduleEndTime = indexAndEndTimes.get(scheduleIndex);

                //根据当前班次的起止时间计算每个值班分组对应的班次，再根据班次（集合）获取成员信息，根据成员数量计算需要展示为几行
                Map<Integer, List<DutySchedule>> groupIdAndSchedules = new Hashtable<>();
                Map<Integer, List<DutyScheduleMember>> groupIdAndScheduleMembers = new Hashtable<>();
                //当前班次需要显示的行数，根据值班分组显示的列数和班次成员的数量计算得到，默认为1行
                int scheduleRowCount = 0x1;

                for (DutyGroupDto group : groups) {
                    int groupId = group.getId();
                    int groupCol = group.getCol();

                    List<DutySchedule> groupSchedules = schedules.stream().filter(p -> p.getGid() == groupId
                            && (p.getBegintm().equals(scheduleBeginTime) || p.getBegintm().after(scheduleBeginTime))
                            && (p.getEndtm().equals(scheduleEndTime) || p.getEndtm().before(scheduleEndTime))
                    ).collect(Collectors.toList());

                    System.out.println(DateUtil.getDateString(scheduleBeginTime) + "~~" + DateUtil.getDateString(scheduleEndTime) + group.getName() + "(" + groupId + "-" + groupCol + ")" + " groupSchedules=" + groupSchedules.size());
                    groupIdAndSchedules.put(groupId, groupSchedules);

                    if (groupSchedules != null && groupSchedules.size() > 0x0) {
                        //在前面已经对排班的起止时间做过最小分段处理，按此条件筛选出来的记录应该只有1条

                        //获取值班班次中的ID，默认取第一个
                        int scheduleId = groupSchedules.get(0x0).getId();
                        //获取班次中的成员数量，用于计算需要显示的行数
                        List<DutyScheduleMember> scheduleMembers = allScheduleMembers.stream().filter(p -> p.getId() == scheduleId).collect(Collectors.toList());

                        groupIdAndScheduleMembers.put(groupId, scheduleMembers);

                        System.out.println(DateUtil.getDateString(scheduleBeginTime) + "~~" + DateUtil.getDateString(scheduleEndTime) + group.getName() + "(" + groupId + "-" + groupCol + ")" + " scheduleId=" + scheduleId + " scheduleMembers=" + scheduleMembers.size());
                        if (scheduleMembers != null && scheduleMembers.size() > 0x0) {
                            int groupMemberRows = scheduleMembers.size() / groupCol;
                            //如果不能整除，有余数，需要多1行显示
                            if (scheduleMembers.size() % groupCol > 0x0)
                                groupMemberRows++;
                            if (groupMemberRows > scheduleRowCount)
                                scheduleRowCount = groupMemberRows;
                        }
                    }
                }

                //根据计算得到的当前班次需要显示的数据行数，生成对应的数据行
                for (int i = 0; i < scheduleRowCount; i++) {
                    QCTableRow row = new QCTableRow();
                    cells = new ArrayList<>();
                    //第一行显示日期、星期；需要根据当前班次显示的行数设置rowspan
                    if (i == 0x0) {
                        cell = new QCTableCell();
                        cell.setRowspan(scheduleRowCount);
                        cell.setColspan(0x1);
                        //日期显示，如果跨月，起始和结束时间均显示月份；如果不跨月只显示起始时间的月份
                        if (DateUtil.getMonth(scheduleBeginTime) == DateUtil.getMonth(scheduleEndTime)) {
                            cell.setText(DateUtil.getMonth(scheduleBeginTime) + "月" + DateUtil.getDay(scheduleBeginTime) + "~" + DateUtil.getDay(scheduleEndTime));
                        } else {
                            cell.setText(DateUtil.getMonth(scheduleBeginTime) + "月" + DateUtil.getDay(scheduleBeginTime) + "~" + DateUtil.getMonth(scheduleEndTime) + "月" + DateUtil.getDay(scheduleEndTime));
                        }
                        cells.add(cell);

                        cell = new QCTableCell();
                        cell.setRowspan(scheduleRowCount);
                        cell.setColspan(0x1);
                        //获取第一个值班分组配置的时间段描述和是否为节假日
                        DutySchedule firstGroupDutySchedule = null;
                        if (groupIdAndSchedules != null && groupIdAndSchedules.size() > 0x0) {
                            //获取第一个值班分组ID
                            int firstGroupId = groups.get(0x0).getId();
                            List<DutySchedule> groupSchedules = groupIdAndSchedules.get(firstGroupId);
                            if (groupSchedules != null && groupSchedules.size() > 0x0) {
                                firstGroupDutySchedule = groupSchedules.get(0x0);
                            }
                        }
                        if (firstGroupDutySchedule != null) {
                            cell.setText(firstGroupDutySchedule.getTmtext());
                            if (firstGroupDutySchedule.getHoliday()) {
                                //如果为节假日，设置样式
                                cell.setStyle("background-color: yellow;");
                            }
                        }

                        cells.add(cell);
                    }

                    //不管是否为第1行信息，均需要对值班成员列所在单元格进行添加；需要遍历分组进行添加，如果没有成员或信息保持单元格空白
                    for (DutyGroupDto group : groups) {
                        int groupId = group.getId();
                        int groupCol = group.getCol();

                        List<DutyScheduleMember> rowGroupMembers = null;
                        //根据排班和成员信息，当前显示的行数（需要跳过的成员记录数、获取的成员记录数）获取本行中当前分组显示的成员信息
                        if (groupIdAndScheduleMembers != null & groupIdAndScheduleMembers.size() > 0x0) {
                            List<DutyScheduleMember> groupScheduleMembers = groupIdAndScheduleMembers.get(groupId);
                            if (groupScheduleMembers != null && groupScheduleMembers.size() > 0x0) {
                                //根据分组、值班时间得到对应的排班ID                   
                                long skipCount = (long) i * groupCol;
                                rowGroupMembers = groupScheduleMembers.stream().skip(skipCount).collect(Collectors.toList());
                            }
                        }

                        //遍历分组，添加分组的值班成员信息
                        for (int j = 0; j < groupCol; j++) {
                            DutyStaffDto dutyStaff = null;
                            if (rowGroupMembers != null && rowGroupMembers.size() > j) {
                                int staffId = rowGroupMembers.get(j).getMember();
                                List<DutyStaffDto> selectedStaffsById = allDutyStaffs.stream().filter(p -> p.getId() == staffId).collect(Collectors.toList());
                                if (selectedStaffsById != null && selectedStaffsById.size() > 0x0)
                                    dutyStaff = selectedStaffsById.get(0x0);
                            }

                            cell = new QCTableCell();
                            cell.setRowspan(0x1);
                            cell.setColspan(0x1);
                            if (dutyStaff != null)
                                cell.setText(dutyStaff.getName());
                            cells.add(cell);

                            cell = new QCTableCell();
                            cell.setRowspan(0x1);
                            cell.setColspan(0x1);
                            if (dutyStaff != null)
                                cell.setText(dutyStaff.getPhone());
                            cells.add(cell);

                            cell = new QCTableCell();
                            cell.setRowspan(0x1);
                            cell.setColspan(0x1);
                            if (dutyStaff != null)
                                cell.setText(dutyStaff.getCornet());
                            cells.add(cell);
                        }
                    }

                    row.setCells(cells);
                    bodyRows.add(row);
                }
            }

            result.setBodyRows(bodyRows);
        }

        return result;
    }
}
